using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize
{
    public unsafe partial struct LStreamReader
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadUnmanagedNumberField<T>(LSerializeContext* context, ref T value) where T : unmanaged
        {
            context->CheckValHeader();
            Seek(context->EnsureValOffset(), SeekOrigin.Begin);
            if (context->Skip > 0)
            {
                GetFormatter<ValueHeader>().Deserialize(context, Ptr, ref *context->ValHeader);
                if (context->ValHeader->Tag == context->Tag)
                {
                    // Ptr->Seek(sizeof(ValueHeader), SeekOrigin.Current);
                    GetFormatter<T>().Deserialize(context, Ptr, ref value);
                    context->DecreaseSkip();
                }
                context->IncreaseTag(); 
            }
            Seek(context->EnsureRefOffset(), SeekOrigin.Begin);
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void ReadNextDepthBegin<T>(LSerializeContext* context)
        {
            // 递增深度
            context->IncreaseDepth(); 
            if (context->OutOfDepth) LSerializeException.ThrowReachedDepthLimit(typeof(T));
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void ReadNextDepthEnd<T>(LSerializeContext* context)
        {
            var size = context->RefHeader->Size;
            // 递减深度
            context->Reset();
            context->DecreaseDepth();
            context->RefHeader->Size = Interlocked.Add(ref context->RefHeader->Size, size); 
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool ReadManagedRefId<T>(LSerializeContext* context, ref T value, out long refId)
        {
            refId = 0L;
            ReadUnmanagedNumberField<long>(context, ref refId);
            if (refId == 0L) return true;
            if (LSerializeExtension.CacheObj.TryGetSerializeObj<T>(refId, out T obj))
            {
                value = obj;
                return true;
            }
            return false;
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadValueBegin<T>(LSerializeContext* context, ref T value, ref long refId)
        {
            Profiler.Start("LStreamReader.ReadValueBegin");
            // 读取引用头
            context->ResetRefHeader();
            context->Head = Seek(0, SeekOrigin.Current);
            GetFormatter<ReferenceHeader>().Deserialize(context, Ptr, ref *context->RefHeader);
            
            // if (0L != refId)
            //     Assert.Check(context->RefHeader->RefId == refId,
            //         $"Read managed value {typeof(T)} refId not match! value refId: {refId}, header refId: {context->RefHeader->RefId}");

            // 重置引用尺寸大小
            context->SetValidFieldCount(context->RefHeader->ValCount);
            context->RefHeader->Size = 0;
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadValueEnd<T> (LSerializeContext* context, ref T value)
        {
            Profiler.Start("LStreamReader.ReadValueEnd");
            // 记录引用尺寸大小
            var size = Seek(0, SeekOrigin.Current) - context->Head;
            context->RefHeader->Size = size;
            // context->RefHeader->Size = Interlocked.Add(ref context->RefHeader->Size, size);
            
            // 缓存引用类型
            LSerializeExtension.CacheObj.CacheSerializeObj(context->RefHeader->RefId, ref value);
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadManagedField<T>(LSerializeContext* context, ref T value)
        {
            Profiler.Start("LStreamReader.ReadManagedField");
            if (!ReadManagedRefId(context, ref value, out var refId))
            {
                // var head = context->EnsureRefOffset();
                // Ptr->Seek(head, SeekOrigin.Begin); 
                // var header = new ReferenceHeader();
                // context->ResetRefHeader(header.Guid, header.ValCount, header.RefCount);
                // GetFormatter<ReferenceHeader>().Deserialize(context, Ptr, ref header);
            
                // // 递增深度
                // context->IncreaseDepth();
                // if (context->OutOfDepth) LSerializeException.ThrowReachedDepthLimit(typeof(T));
            
                ReadNextDepthBegin<T>(context);
                ReadManagedValue<T>(context, ref value, refId);
                ReadNextDepthEnd<T>(context);
            
                // int size = context->RefHeader->Size;
                //
                // // 递减深度
                // context->Reset();
                // context->DecreaseDepth();
                //
                // context->RefHeader->Size = Interlocked.Add(ref context->RefHeader->Size, size); 
            }
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadManagedValue<T>(LSerializeContext* context, ref T value, long refId = 0L)
        {
            Profiler.Start("LStreamReader.ReadManagedValue");
            ReadValueBegin<T>(context, ref value, ref refId);
            
            var curValHeader = context->CurValHeader;
            if (null != curValHeader && curValHeader->ValType == FieldType.Class && curValHeader->CollectionType == CollectionType.None)
            {
                Profiler.Start("LStreamReader.ReadRealType");
                var flag = Seek(0, SeekOrigin.Current);
                Type type = null;
                GetFormatter<Type>().Deserialize(context, Ptr, ref type);
                // string typeName = ReadString();
                // Console.WriteLine($"typeName: {typeName}");
                context->ValOffset = Seek(0, SeekOrigin.Current) - flag;
                Profiler.End();
                
                // Type type = Type.GetType(typeName);
                if (null != type && type.IsSubclassOf(typeof(T)))
                {
                    Profiler.Start("LStreamReader.ReadRealValue");
                    object v = value;
                    GetFormatter(type).Deserialize(context, Ptr, ref v);
                    value = (T) v;
                    Profiler.End();
                }
                else
                {
                    Profiler.Start("LStreamReader.ReadManagedSource");
                    GetFormatter<T>().Deserialize(context, Ptr, ref value);
                    Profiler.End();
                }
            }
            else
            {
                Profiler.Start("LStreamReader.ReadManagedSource");
                GetFormatter<T>().Deserialize(context, Ptr, ref value);
                Profiler.End();
            }

            ReadValueEnd<T>(context, ref value);
            Profiler.End();
        }
    }
}